package md.cm.flow;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import md.log.Circulation;
import md.specialEqp.inspect.Detail;
import md.specialEqp.inspect.Procedure_Enum;
import md.system.User;
import org.fjsei.yewu.filter.Uunode;
import org.fjsei.yewu.util.Tool;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import jakarta.persistence.*;
import java.util.*;

import static md.specialEqp.inspect.Procedure_Enum.BEGIN;

/**流程引擎的配套配置和跟踪实体。
 * 审批流转状态机抽象   # 【zeebe引进后】需改成从流程引擎服务去提供UserTask任务列表了。
 * 考虑改成：辅助作用，历史记录的跟踪。但是zeebe引擎才是真正的服务提供者，这个实体及关联记录蜕变成备份和跟踪。zeebe存储的记录不做长期留存的。
 通用审批模型， 依靠关联从具体多种多样的审批单或报告实体模型脱离出来。
 申请单或者报告。
 一般简单的申请状态组合是 Procedure_Enum【 MAKE正在申请当中, APPR提交给审批人 ,END审批完成, CANCEL取消 】就能应付需求。
 一般审批模型，另外添加一个字段： check_status:=1申请批准，0申请不被批准 && 流程状态=Procedure_Enum.END。
 通用模型Auditflow,如何定制 node role{数据库内容依赖} Action{接口处理函数}
 也能当成投票器，支持对某个主体/报告内容的签字或举手表决，全部签字通过才算数，还是大多数人赞成就能终结流转下去了。
 【约束】状态步骤环节预定义好的，外部逻辑控制流程，流转历史回撤按时间和环节标记来追溯后退。参考String  flowReport(Long repId,Boolean back,String memo,List<Long> userIds);
 需要提供给zeebe流程引擎的流程变量可以在此保存和设置； 每一个报告或审批事项单子都会要设置一个独立的ApprovalStm；
 */


@NoArgsConstructor
@AllArgsConstructor
@Entity
@Getter
@Setter
public class ApprovalStm implements Uunode {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private UUID id;

    @Version
    private int  version;

    //申请单或者报告。
    //一般简单的申请状态组合是 Procedure_Enum【 MAKE正在申请当中, APPR提交给审批人 ,END审批完成, CANCEL取消 】就能应付需求。
    //一般审批模型，另外添加一个字段： check_status:=1申请批准，0申请不被批准 && 流程状态=Procedure_Enum.END。
    /**当前流程节点; 自定义状态代号/后端数字代号Enum-前端中文表达。
     状态转移关联记录模式。Auditflow 业务含义阶段状态 status，
     子报告只有两步操作，流转单独审核{审核人员自选不是派工时刻就指定的审核人}，单独审核不通过回退编制{派工时检验员多人}，单独审核通过并流转组合{子报告分支流程结束}；
     子报告特有环节= 等待与主报告组合和审核103。
     有不合格结论导致并且需要复检的情况， 流转102审核通过后[允许不合格复检]有个复检任务再做派工？{Task任务状态修订},流转复检环节103;复检后再次101编制？
     等待整改反馈期内的复检不收费，过了之后要收费。复检判断 如果复检派工的时间小于等待整改反馈期，则取原来的报告号=Isp；
     等待整改反馈期内的复检就不需要Task终结了再派工另外一个新的Task(初次检验未能通过的部分设备),直接对未终结Task重复派工(任务标记复检，等待复检的设备，部分Isp终结)。
     并流转到责任工程师组合,再由主报告带着一起流转审核(子报告分解目的已经结束)；主报告和分项报告节点status不一致错误;
     报告签发了终结，异常可注销。
     复检环节标记。
     最终都要通关吧，才能算目的达到了 END终结。
     还没有真实去做检验工作就允许编制报告啊，流程缺少一个控制环节。
     在报告流转添加个前置0环节，针对会议投票或者报告编制的关联状态机必须手动开启，不能直接自动开启。
     zeebe流程图上节点和这个没有绝对对应关系，这个状态代表大概的关卡也就是流转审批进度。
     */
    private Procedure_Enum sta =BEGIN;

    /**检验人员IspMens[]；报告流转场景，就是检验人员(准备签字的)
     * 允许共同编辑报告的人。跟随制作报告的检验员多人。 上报给监察平台的检验员多个。
     * 提请签字人，多人要签字； 允许编制报告的检验员。
     * 节点一般权利人；
     * 当前流程节点授权角色的用户。 状态转换后，用户就变动了。
     * master引入为了简化节点任务的控制，控制流转义务人，流转人和节点一般权利人分离开了。
     当前节点用户权限, 对当前模型实体(报告、申请单)的操作权限，允许修改作用于实体。
     authorizationUsers设置的同时，增加Message[user]{linkURL指代}提醒/站内短信。
     派工：分项报告的检验员可以和主报告的检验员不一样的，可选多人。协检员是报告显示用的，协检员不参与后端逻辑。
     子报告编制节点可多人都允许编辑报告。 子报告审核节点可允许多人都有权利审核(同时审核有个人决定通过还是不通过都算触发流转，不需要每个人都做审核，当中一人就行)
     主报告审批环节还可以插脚限制流转或审核意见不一致导致回退。
     报告交付后，谁有权阅读报告，监管，历史报告查看不做角色控制；
     @ManyToMany 若有多个同样两个实体的关联的，必须加上name，否则自动生成中间表只有一个合并的字段，关系不正常。
     若多对多非维护关系的对方也有声明字段的需要用@ManyToMany(mappedBy="devs")类似定义。 @ManyToMany两实体并非有同等待遇。PRIMARY KEY (apstid ASC, userid ASC)；
     【问题】@ManyToMany中间表默认索引定义看出:维护方实体在组合索引中优先顺序，查询性能优先于非维护方的实体，非维护方这里是User类若从User查询本关系User.approvalStm[]性能堪忧。
     JPA正常ID都是单字段，只有ManyToMany关联中间表才会生成复合主键Composite key PRIMARY KEY (apstid ASC, userid ASC),复合多字段主键若是外键关联也需要多个字段配套啊。
     @ManyToMany 主键索引时有排列关系的多字段，类比其它多字段的索引；【注意】查询若是第二个字段单独做过滤，就不走默认主键索引，全表搜索？需要人工加上更多索引。
     多人一起签名，要求每个人都需要签字同意才允许真正流转下一个节点。
     投票会议初始总人数(初始化有资格签名人员列表)，每个人签字后authr直接删除已经投票User。
     报告编制环节，authr=所有输入原始记录和修改报告人=所有要签字人。
     单人审批的节点authr就一个人{从角色挑选一个User}。
     分项报告也能独立选择检验人员+协检人员。 master不一定在authr之中的。
     容器：分项报告：需要证书的情况？ 编制人员必须在authr[]本报告之中；提取原始记录填写时刻的业务人资格证书。
     */
    @ManyToMany
    @JoinTable(name="ApprStm_authrUser",joinColumns={@JoinColumn(name="apstId")},inverseJoinColumns={@JoinColumn(name="userId")})
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL,region ="Fast")
    private List<User> authr;

    /**时间最新那一条 流转 审批log;
     一个单子可能多次审批流转， 最新的结论是。
     报告流转不是看结论，报告目的是每一关都作业确认完结，流转有多个关口。
     普通审批单子是看结论的，审批一级的只需要一个关口，除非是多级二级审核审批的单子才需要多个关口。
     不管几个关卡口，最终都要通关吧，才能算目的达到了。
     普通审批单子流转到END, 审批意见在cur.opinion。普通审批单子也就两条Circulation记录。
     流转有权限的人，不一定在authr中，报告编制和签名投票节点master才是有权利流转的人。
     对方实体不关心它的没有做@OneToOne； 仅仅在这里做了 单方向关联 标注。
     */
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn( referencedColumnName = "ID")
    private Circulation  cur;

    /**流转或审核的操作明细Log。 支持后退。
       每个环节时间进度超期预警。
     关联操作历史记录AuditOperate<(status,time,user,OPTION通过退回,MEMO,处理后转为Nextstatus,当前状态哪些Users可处理)>;
     @OneToMany和多对多的 Set<>注解 本来就是FetchType 默认=LAZY
     不是zeebe流程全部走向记录，主要是登记用户操作的历史，时间排序。
     * */
    @OneToMany(mappedBy = "apply")
    private  List<Circulation> his =new ArrayList<>();

     /*纯粹类函数，非实体存储用。
    注意：@Autowired 不可以直接放入@Entity 模型类当中！
    非controller或service类中 用@Autowired直接注入是不可能成功的；
     * 流转动作 无法直接放在 实体当中。
    */

    /**校核人 发起人; 报告校核人。负责流转第一个人；zeebe创建实例的人。负责人；
     * 特别权利人： 状态机的 初始状态的控制人, 复杂节点的流转义务人；
     * master引入为了简化节点任务的控制，控制流转义务人，复杂节点的流转人和节点一般权利人分离开了。
     * 报告编制、签名或投票的节点需要主持人校核人，编制人员,签名人,投票人人数可多个人，主控人只有一个人。
     * 报告的校核人；
     * 普通申请单子的发起人； 甚至某一次投票主题目的会议主持人。
     * 只有单一个User; 流转状态机的首要控制人。
     * 多人签字协调用； 强制撤回删除用。
     * 主持人主控人 不一定在节点的授权用户authr中。
     * 主持人在状态机建立后基本不变了。
     * 但是当前节点授权人authr是变化的列表，每个环节变化，签名环节甚至节点内也会变少而签字后节点保持是签名节点{签名不算真流转}。
     * 这算是当前节点的权限分离或者说实际上的节点权利分解你两阶段的融合，一个是受监督的权利义务，另外一个是监督者负责转移环节，合并一个节点。
     * 看似: 报告编制=？ 检验多人录入 =>流转校核人 =>校核人下结论流转签字。
     * 看似: 签名环节=？ 多个签字人确认 =>最后一个非校核人的签字人才需要触发流转校核人 =>校核人自己签字才真正签字完毕流转CHECK审核节点。
     * 所以实际上都可分解为两个标准意义的流程节点，现实上简化合并了添加master校核人来控制流转义务人,复杂节点的流转人和节点一般权利人分开。
     * 其它普通节点环节可以不受master所影响，继续使用authr。
     * 允许控制人也转移权利给下一个控制人,可移交撒手不管。
     */
    @ManyToOne(fetch= FetchType.LAZY)
    private User master;

    /**节点预计时间到期 期限。 zeebe需要在UserTask做超时等待机制的时刻配置。
     * authr初始总人数(初始化有资格签名人员列表)，
     * 投票的人，赞成 反对 弃权票人数:可直接 统计his获得。
     * 投票环节截止时间点，还没有投票的人数就是authr剩余集合。
     * 投票结束时间：master统计票数，决定流转END终结,同时给出流转意见(投票汇总结果)。
     * 流转审批 要设置节点有效期限吗？ 过期撤回。
     * 没有终结或删除的状态机+due有日期且超期的，发消息通知authr{会议投票的除外}。
     * */
    @Temporal(TemporalType.DATE)
    private Date  due;

    /**超期的发通知消息的，上次发送时间，#对接的是 外部消息系统对接 @发短信？。
     * 不要频繁发消息。
     * */
    @Temporal(TemporalType.DATE)
    private Date  lastSend;

    /**审核人 reviewer； 第一关审批人。 责任工程师审核阶段的审核人。
     * 责任工程师
     */
    @ManyToOne(fetch= FetchType.LAZY)
    private User reviewer;

    /**审批人； 最后最高批准人, 双关卡 多重审批情况。
     * 前端获悉 本部门的组角色定位到人？独立查询，页面选定个人。在哪一个时机选择设置这个审批人。
     * 若单人关 核准的情况下：reviewer必须有，而approver没有的。
     */
    @ManyToOne(fetch= FetchType.LAZY)
    private User approver;
    /**流程实例ID 流程引擎Zeebe返回的关键字。
     * 万一这个流程被遗忘了呢，成垃圾数据，可能影响zeebe性能。？清理旧的流程实例ID
     * zeebe数据被清理之后，旧的ID也可能失效了，不是原来的东东++递增：两个系统间的同步失效。
     * */
    private Long prId;
}




/*
最普通的审批单流程：(两次流转)
 User 新建 申请单， 状态机节点Procedure_Enum.MAKE；
 输入数据和 关联，申请理由，完成申请编制了，保存；
 申请单的允许编辑和提交的角色权限控制= User本人。
 提交申请单，就是流转到 审核人，可选择审批角色人员(多人其中一个人审核就可)；
 提交申请后=Procedure_Enum.APPR,
 提交流转发送消息，审核审批人获得通知，来查看申请单列表；
 审批人浏览页面数据， 允许审核 甚至保密不公开申请信息？ 的人员是授权角色控制。
 审批人点击审核意见，备注，决定通过 不通过，等意见后，点击确认审批完成，流转Procedure_Enum.END；
 END申请单正常的结束，流转END批准通过后，可能会在后台预定义的动作触发了,发消息通知申请人让人知晓结果。
 END后申请单无法修改变为备案历史，只能查看申请的内容，或授权他人可以查看批准的内容。
 CANCEL异常结束=删除/一直是Procedure_Enum.MAKE状态的超过合理期限被迫删除。
Web多个人同时都要对一个报告内容真实性完整性不可篡改性的保证进行，签字，签名流程：
 User登录，查看待签名报告内容，自己一个人签字。

 */
/*
工作流引擎：JBPM 现在发展的也很不错，还有目前比较新兴的流程引擎：camunda，flowable 目前看起来也潜力十足，他们都是遵循了 BPMN 2.0 规范，
因为在国内应用的比较主流，Activiti;camunda-engine;flowable-engine 三个包都有脆弱性疑点：要求H2数据库相关的。
* */


/* @数据库修改脚本：
CREATE INDEX approvalstm_sta_idx ON public.approvalstm USING btree (sta ASC) STORING (master_id);
CREATE INDEX approvalstm_cur_id_index ON public.approvalstm USING btree (cur_id ASC);
    中间表：（需单一个事务之内完成的） List<>默认生成情况。
    BEGIN;
    ALTER TABLE public.apprstm_authruser DROP CONSTRAINT apprstm_authruser_pkey;
    ALTER TABLE public.apprstm_authruser ADD CONSTRAINT "primary" PRIMARY KEY (apstid, userid);
    COMMIT;
    alter table public.apprstm_authruser  drop column rowid;
    这步执行后，就会和Set<>默认生成情况一致了。
* */
